package com.fingard.dsp.bank.directbank.pab02;

import cfca.sadk.org.bouncycastle.util.io.pem.PemObject;
import cfca.sadk.org.bouncycastle.util.io.pem.PemObjectGenerator;
import cfca.sadk.org.bouncycastle.util.io.pem.PemWriter;
import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import com.fingard.constant.Format;
import com.fingard.dsp.bank.directbank.DirectBase;
import com.fingard.dsp.bank.directbank.PaymentState;
import com.fingard.dsp.bank.directbank.fc11.util.RSACoder;
import com.fingard.dsp.bank.directbank.pab02.Util.api.bean.FileMsgBean;
import com.fingard.dsp.bank.directbank.pab02.Util.api.bean.FtpGet;
import com.fingard.dsp.bank.directbank.pab02.Util.api.bean.FtpPut;
import com.fingard.dsp.bank.directbank.vbao02.util.RSAUtil;
import com.fingard.dsp.bank.reqrespobj.batpayrec.RetAtsBatPayRec;
import com.fingard.io.FileHelper;
import com.fingard.net.WebRequest;
import com.fingard.text.StringHelper;
import org.apache.commons.codec.binary.Base64;

import java.io.*;
import java.security.*;
import java.security.cert.Certificate;
import java.security.cert.X509Certificate;
import java.util.*;

/**
 * @author XueJy on 2019/2/14.
 * @version 1.0
 */
public class PAB02Base extends DirectBase {

    protected String getCharset() {
        return getBankConfig().getCharset("GBK");
    }

    String[] WriteCommonHeader(Map<String, Object> paramMap, String txnCode, String applicationID) throws Exception {
        /*开放平台门户申请的AppAccessKey 银行提供*/
        paramMap.put("ApplicationID", applicationID);
        String[] token = getToken(applicationID);
        if (token[0].length() == 0) {
            paramMap.put("AppAccessToken", token[1]);
        }
        paramMap.put("RequestMode", "json");
        paramMap.put("SDKType", "api");
        paramMap.put("SdkSeid", getBankConfig().getValueAsString("sdkSeid"));
        paramMap.put("SdkVersionNo", "1.0.6");
        paramMap.put("ApiVersionNo", "1.0.0");
        paramMap.put("TxnTime", Format.DateTime14Format.format(new Date()) + "000");

        paramMap.put("ValidTerm", Format.Date8Format.format(new Date()));
        paramMap.put("TxnCode", txnCode);

        return token;
    }


    /**
     * Step1  获取银行token
     */
    String[] getToken(String applicationID) throws Exception {
        String tokenPath = getCfgDirectory() + File.separator + applicationID + ".txt";
        String token = FileHelper.readFileText(tokenPath, getCharset());
        if (token != null && !"".equals(token)) {
            return new String[]{"", token};
        }
        Map<String, Object> tokenReq = new TreeMap<String, Object>();
        Map<String, Object> map = initSigns(bankFront.ownKeyPassword, bankFront.ownKeyStorePath);
        /*开放平台门户申请的AppAccessKey*/
        tokenReq.put("ApplicationID", applicationID);
        /*接入方式 固定填写 api*/
        tokenReq.put("SDKType", "api");
        tokenReq.put("RandomNumber", get_nonce_str());
        tokenReq.put("cerSeqNo", getcerSeqNo(bankFront.oppCertFilePath));
        tokenReq.put("certPeriod", map.get("DATE").toString());
        JSONObject jsonObject = new JSONObject(tokenReq);
        jsonObject.put("RsaSign", getSign(jsonObject).replaceAll("\r", "").replaceAll("\n", "").replace("+", "%2B").replace("=", "%3D"));
        //读取证书中的DN与公钥
        Principal dn = (Principal) map.get("DN");
        jsonObject.put("DN", dn.toString().replaceAll("=", "%3D"));
        String publickey = (String) map.get("PK");
        jsonObject.put("PK", publickey.replace("+", "%2B"));
        String[] retRes = sendToBank(jsonObject.toJSONString(), bankFront.getUrl("GetTokenUrl"));
        if (retRes[0].length() == 0) {
            JSONObject retJson = JSONObject.parseObject(retRes[1]);
            token = retJson.getString("appAccessToken");
            String retCode = retJson.getString("errorCode");
            String retMsg = retJson.getString("errorMsg");
            if ("OPEN-E-000000".equals(retCode)) {
                if (verifySign(retJson)) {
                    FileHelper.writeFile(tokenPath, token, getCharset());
                    retRes[1] = token;
                } else {
                    retRes[0] = "-1";
                    retRes[1] = "签名验证失败";
                }
            } else {
                retRes[0] = "-1";
                retRes[1] = retCode + "+" + retMsg;
            }
        }

        return retRes;
    }

    /*上传文件*/
    String[] putFileToServer(FileMsgBean fmb) {
        String[] retMsg = new String[]{"", ""};
        FtpPut ftp = null;
        try {
            ftp = new FtpPut(fmb);
            WriteBankLogLn("开始上传文件" + fmb.getLocalPath());
            WriteBankLogLn("发送地址【" + fmb.getFileServerUrl() + "】");

            FileMsgBean retBean = ftp.doPutFile();

            WriteBankLogLn("文件" + fmb.getLocalPath() + "上传成功，返回提取码为:" + retBean.getPrivateAuth());
            retMsg[1] = retBean.getPrivateAuth();
        } catch (Exception e) {
            retMsg[0] = "-1";
            retMsg[1] = fmb.getFileRetMsg();
            WriteBankLogLn("文件" + fmb.getLocalPath() + "上传失败", e);
        } finally {
            if (null != ftp) {
                ftp.close(true);
            }
        }
        return retMsg;
    }

    /*下载文件*/
    String[] getFileFromServer(FileMsgBean fmb) {
        String[] retMsg = new String[]{"", ""};
        FtpGet ftp = null;
        try {
            ftp = new FtpGet(fmb);
            WriteBankLogLn("开始下载文件" + fmb.getLocalPath());
            WriteBankLogLn("发送地址【" + fmb.getFileServerUrl() + "】");
            boolean isFinished = ftp.doGetFile();

        } catch (Exception e) {
            retMsg[0] = "-1";
            retMsg[1] = fmb.getFileRetMsg();
            WriteBankLogLn("文件" + fmb.getRemotePath() + "下载", e);
        } finally {
            if (null != ftp) {
                ftp.close(true);
            }
        }
        return retMsg;
    }


    /*验证配置是否完全*/
    boolean validateConfig(Map<String, String> map, RetAtsBatPayRec retAts) {

        Set<String> keySet = map.keySet();
        StringBuilder sb = new StringBuilder();

        for (String key : keySet) {
            String param = map.get(key);
            if (param == null || param.length() == 0) {
                sb.append("未配置：").append(key);
            }
        }
        if ("".equals(sb.toString())) {
            return true;
        } else {
            retAts.respInfo = sb.toString();
            retAts.respCode = "0";
            retAts.transState = PaymentState.FAIL_Payment;
            return false;
        }

    }

    /*验签*/
    public boolean verifySign(JSONObject plain) throws Exception {
        String rsaSign = plain.getString("RsaSign");
        plain.remove("RsaSign");
        // 排序
        Map<String, Object> map = new HashMap<String, Object>();
        /*除去空值*/
        getNeedMap(plain, map);
        /*除去不加入签名的值*/
        map.remove("errorCode");
        map.remove("errorMsg");
        String preStr = StringHelper.mapToString(map,false);
        return verifySignByMD5(preStr.getBytes("UTF-8"), Base64.decodeBase64(rsaSign.getBytes("UTF-8")), bankFront.oppCertFilePath);
    }

    /*  MD5 验签 */
    public static boolean verifySignByMD5(byte[] msgBuf, byte[] sign, String pubKeyPath) {
        Signature signature = null;
        try {
            signature = Signature.getInstance("MD5withRSA");
            PublicKey publicKey = RSAUtil.getRSAPublicKeyByFileSuffix(pubKeyPath, null, "RSA");
            signature.initVerify(publicKey);
        } catch (Exception e) {
            throw new RuntimeException("INVALID_PUBKEY");
        }
        try {
            signature.update(msgBuf);
            return signature.verify(sign);
        } catch (SignatureException e) {
            throw new RuntimeException("签名格式不合法");
        }
    }




    /**
     * 取JSON到map
     *
     * @param jsonObj
     * @param map
     */
    private void getNeedMap(JSONObject jsonObj, Map<String, Object> map) {
        for (String key : jsonObj.keySet()) {
            Object object = jsonObj.get(key);
            if (object instanceof JSONObject) {
                JSONObject Obj = (JSONObject) object;
                // getNeedMap(Obj, map);
                getNeedMap(key, (JSONObject) Obj, map);
            } else if (object instanceof JSONArray) {
                JSONArray jsonArray = (JSONArray) object;
                int size = jsonArray.size();
                for (int i = 0; i < size; i++) {
                    Object arrayObject = jsonArray.get(i);
                    if (arrayObject instanceof String) {
                        map.put((String) arrayObject, (String) arrayObject);
                    } else {
                        getNeedMap(key, (JSONObject) arrayObject, map);
                    }
                }
            } else if (object instanceof String) {
                map.put(key, (String) object);
            }
            continue;
        }
    }

    /*
     * 取JSON到map
     */
    private void getNeedMap(String parent, JSONObject jsonObj, Map<String, Object> map) {
        for (String key : jsonObj.keySet()) {
            Object object = jsonObj.get(key);
            if (object instanceof JSONObject) {
                JSONObject Obj = (JSONObject) object;
                getNeedMap(parent.concat(key), (JSONObject) Obj, map);
                // getNeedMap(Obj, map);
            } else if (object instanceof JSONArray) {
                JSONArray jsonArray = (JSONArray) object;
                int size = jsonArray.size();
                for (int i = 0; i < size; i++) {
                    Object arrayObject = jsonArray.get(i);
                    if (arrayObject instanceof String) {
                        map.put(parent.concat((String) arrayObject), (String) arrayObject);
                    } else {
                        getNeedMap(parent.concat(key), (JSONObject) arrayObject, map);
                        // getNeedMap((JSONObject)arrayObject, map);
                    }
                }
            } else if (object instanceof String) {
                map.put(parent.concat(key), (String) object);
            }
            continue;
        }
    }




    /**
     * 签名
     */
    String getSign(Map<String, Object> tokenReq) {
        String tmpStr = StringHelper.mapToString(tokenReq,false);
        String sign = "";
        try {
            WriteBankLogLn("待签名字段："+tmpStr);
            byte[] data = tmpStr.getBytes("UTF-8");
            PrivateKey privateKey = RSAUtil.getRSAPrivateKeyByFileSuffix(bankFront.ownKeyStorePath, null, bankFront.ownKeyPassword, "RSA");
            sign = RSACoder.sign(data, privateKey, "MD5withRSA");
        } catch (Exception e) {
            WriteBankLogLn("签名失败", e);
        }
        return sign;

    }

    /**
     * 初始化签名
     *
     * @param passwd 证书密码
     * @param path   自己证书路径
     * @return DN, PK
     */
    public Map<String, Object> initSigns(String passwd, String path) {

        Map<String, Object> signMap = new HashMap<String, Object>();
        FileInputStream fis = null;
        try {
            KeyStore inputKeyStore = KeyStore.getInstance("PKCS12");
            fis = new FileInputStream(path);
            char[] nPassword = passwd.toCharArray();
            Principal dn = null;
            inputKeyStore.load(fis, nPassword);
            Enumeration<?> enums = inputKeyStore.aliases();
            String publickey = "";
            Date certPeriod = null;
            while (enums.hasMoreElements()) {
                String keyAlias = (String) enums.nextElement();
                if (inputKeyStore.isKeyEntry(keyAlias)) {
                    Key key = inputKeyStore.getKey(keyAlias, nPassword);
                    Certificate cert = inputKeyStore.getCertificate(keyAlias);
                    PublicKey pK = cert.getPublicKey();
                    X509Certificate cc = (X509Certificate) cert;
                    dn = cc.getSubjectX500Principal();
                    publickey = getKeyString(pK);
                    certPeriod = cc.getNotAfter();
                }
            }

            signMap.put("DN", dn);
            signMap.put("PK", publickey);
            signMap.put("DATE", certPeriod);

        } catch (Exception e) {
            WriteBankLogLn("密钥初始化失败", e);
        } finally {
            if (fis != null) {
                try {
                    fis.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
        return signMap;
    }


    private String getKeyString(Key key) {
        StringWriter stringWriter = new StringWriter();
        String keyReturn = "";
        try {
            PemWriter writer = new PemWriter(stringWriter);
            String line = "";
            String replace1 = "";
            String replace2 = "";
            line = "PUBLIC  KEY";
            replace1 = "-----BEGIN PUBLIC  KEY-----";
            replace2 = "-----END PUBLIC  KEY-----";
            PemObjectGenerator pemObject = new PemObject(line, key.getEncoded());
            writer.writeObject(pemObject);
            writer.close();
            keyReturn = stringWriter.toString().replaceAll("\r", "").replaceAll("\n", "")
                    .replace(replace1, "").replace(replace2, "");
        } catch (Exception e) {
            WriteBankLogLn("公钥读取异常", e);
        }
        return keyReturn;
    }


    /**
     * @return
     */
    protected  String get_nonce_str() {
        //随机字符串 String(32)
        Random random = new Random();
        Integer randLong = random.nextInt(999999);
        String zero = "000000".substring(0, (6 - randLong.toString().length()));
        String nonce_str = zero + randLong.toString();
        return nonce_str;
    }

    /**
     * 发送银行
     */
    protected String[] sendToBank(String sendMsg, String url) throws Exception {
        String[] retStr;
        WriteBankUrlLogLn(url);
        WriteBankLogStep2(sendMsg);
        WebRequest tmpWebRequest = new WebRequest(url);
        tmpWebRequest.setConnectTimeout(60000);
        tmpWebRequest.setRequestProperty("connection", "Close");
        byte[] tmpUpBytes = sendMsg.getBytes(getCharset());
        retStr = tmpWebRequest.upload(tmpUpBytes, "UTF-8");
        WriteBankLogStep3(retStr[1]);
        if (retStr[0].length() == 0) {
            int tmpIndex = retStr[1].indexOf("{");
            if (tmpIndex > 0) {
                retStr[1] = retStr[1].substring(tmpIndex);
            }
        } else {
            this.WriteBankLogLn(tmpWebRequest.sbLog);
        }
        return retStr;
    }

    /**
     * 开放平台公钥序列号
     */
    public String getcerSeqNo(String path){
        String cerSeqNo="";
        FileInputStream fis = null;
        File file =new File(path);
        try {
            java.security.cert.CertificateFactory cert=java.security.cert.CertificateFactory.getInstance("X.509");
            fis =new FileInputStream(file);
            X509Certificate Cert =(X509Certificate) cert.generateCertificate(fis);
            cerSeqNo=Cert.getSerialNumber().toString(16);
        } catch (FileNotFoundException e) {
            WriteBankLogLn("证书公钥cer文件找不到", e);
        } catch (Exception e) {
            WriteBankLogLn("读取证书公钥cer错误", e);
        }
        return cerSeqNo;
    }


}
